﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Buffer = SharpDX.Direct3D11.Buffer;
using Device = SharpDX.Direct3D11.Device;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Direct3D;

namespace Ignitron.EEngine.Adapters.DX.Graphics
{
    /// <summary>
    /// Primitives renderer
    /// </summary>
    internal class PrimitivesRenderer
    {
        /// <summary>
        /// The shader used for rendering primitives
        /// </summary>
        private ColorShader mShader;

        /// <summary>
        /// D3D Device
        /// </summary>
        private Device mDevice;

        /// <summary>
        /// Rendering window handle
        /// </summary>
        private IntPtr mWindowHandle;

        /// <summary>
        /// Transformatio matrices
        /// </summary>
        private Matrix mViewMatrix, mWorldMatrix, mProjectionMatrix;

        /// <summary>
        /// Vertex list for queueing the rendered primitives
        /// </summary>
        private List<ColorShader.Vertex> mVertexList;


        /// <summary>
        /// Initialization method
        /// </summary>
        public bool Initialize(Device device, IntPtr windowHandle)
        {
            // set params
            mDevice = device;
            mWindowHandle = windowHandle;

            // initialize shader
            mShader = new ColorShader();
            if (mShader.Initialize(mDevice, mWindowHandle, "Color.vs", "Color.ps") == false)
            {
                return false;
            }

            // initialize buffer for rendering vertices
            mVertexList = new List<ColorShader.Vertex>();

            return true;                      
        }

        /// <summary>
        /// Shutdown routine
        /// </summary>
        public void Shutdown()
        {
            // shutdown shader
            if (mShader != null)
            {
                mShader.Shuddown();
                mShader = null;
            }
        }

        /// <summary>
        /// Sets the world, view and projection matrices
        /// </summary>
        public void SetTransformMatrices(Matrix world, Matrix view, Matrix projection)
        {
            mWorldMatrix = world;
            mViewMatrix = view;
            mProjectionMatrix = projection;

            mShader.SetTransformMatrices(mWorldMatrix, mViewMatrix, mProjectionMatrix);
        }

        /// <summary>
        /// Begins rendering points
        /// </summary>
        public void BeginPoints()
        {
            // clears the vertex list
            mVertexList.Clear();
        }

        /// <summary>
        /// Renders a point
        /// </summary>
        public void RenderPoint(Vector3 p, Vector4 color)
        {
            mVertexList.Add(new ColorShader.Vertex()
            {
                position = new Vector4()
                {
                    X = p.X,
                    Y = p.Y,
                    Z = p.Z,
                    W = 1f
                },
                color = new Vector4()
                {
                    X = color.X,
                    Y = color.Y,
                    Z = color.Z,
                    W = color.W
                }
            });
        }

        /// <summary>
        /// Ends rendering points
        /// </summary>
        public void EndPoints()
        {

            // create the buffer
            Buffer vertexBuffer = Buffer.Create<ColorShader.Vertex>(mDevice, BindFlags.VertexBuffer, mVertexList.ToArray());

            // get the device context
            DeviceContext deviceContext = mDevice.ImmediateContext;
            // Set the vertex buffer to active in the input assembler so it can be rendered.
            deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, Utilities.SizeOf<ColorShader.Vertex>(), 0));
            // Set the type of the primitive that should be rendered from this vertex buffer, in this case triangles.
            deviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;

            // render all vertices
            RenderShader(mVertexList.Count);

            // dispose the buffer
            vertexBuffer.Dispose();
        }

        /// <summary>
        /// Begins the lines
        /// </summary>
        public void BeginLines()
        {
            mVertexList.Clear();
        }
        
        /// <summary>
        /// Renders a line
        /// </summary>
        public void RenderLine(Vector3 v0, Vector3 v1, Vector4 color)
        {
            // add the points to the list
            mVertexList.Add(new ColorShader.Vertex()
            {
                position = new Vector4()
                {
                    X = v0.X,
                    Y = v0.Y,
                    Z = v0.Z,
                    W = 1f
                },
                color = new Vector4()
                {
                    X = color.X,
                    Y = color.Y,
                    Z = color.Z,
                    W = color.W
                }
            });

            mVertexList.Add(new ColorShader.Vertex()
            {
                position = new Vector4()
                {
                    X = v1.X,
                    Y = v1.Y,
                    Z = v1.Z,
                    W = 1f
                },
                color = new Vector4()
                {
                    X = color.X,
                    Y = color.Y,
                    Z = color.Z,
                    W = color.W
                }
            });        
        }

        /// <summary>
        /// Ends rendering queue for the lines
        /// </summary>
        public void EndLines()
        {
            // create the buffer
            Buffer vertexBuffer = Buffer.Create<ColorShader.Vertex>(mDevice, BindFlags.VertexBuffer, mVertexList.ToArray());

            // finally render
            DeviceContext deviceContext = mDevice.ImmediateContext;
            // Set the vertex buffer to active in the input assembler so it can be rendered.
            deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, Utilities.SizeOf<ColorShader.Vertex>(), 0));
            // Set the type of the primitive that should be rendered from this vertex buffer, in this case triangles.
            deviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineList;

            // render two vertices
            RenderShader(mVertexList.Count);

            // free the buffer
            vertexBuffer.Dispose();

        }

        /// <summary>
        /// Finally render the primitives using the shader
        /// </summary>
        private void RenderShader(int vertexCount)
        {
            mShader.Render(vertexCount);
        }
    }
}
